package com.sk.util;

import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 压缩工具。
 *
 * @author smy
 * {@code @date} 2022/11/16
 */
@Slf4j
public class FileZipUtil {

    /**
     * 将文件目录做成压缩文件
     *
     * @param srcDir 文件目录
     * @return 压缩文件
     */
    public static File zipDir(File srcDir) {
        return zipDir(srcDir, true, (dir, s) -> true);
    }

    /**
     * 将文件目录做成压缩文件
     *
     * @param srcDir     文件目录
     * @param includeTop 压缩是否包含顶级目录名
     * @return 压缩文件
     */
    public static File zipDir(File srcDir, boolean includeTop) {
        return zipDir(srcDir, includeTop, (dir, s) -> true);
    }

    @SneakyThrows
    public static File zipDir(File srcDir, boolean includeTop, FilenameFilter filter) {
        File zip = new File(srcDir.getPath() + ".zip");
        @Cleanup ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zip.toPath()));
        __compress(includeTop ? srcDir.getName() : "", srcDir, zos, filter);
        return zip;
    }

    private static void __compress(String key, File file, ZipOutputStream zos, FilenameFilter filter) throws IOException {
        if (StringUtils.hasLength(key)) {
            key += FileUtil.separator;
        }
        if (file.isFile()) {
            zos.putNextEntry(new ZipEntry(key));
            FileUtil.copyStream(FileUtil.inputStream(file), zos);
            zos.closeEntry();
        } else {
            key += file.getName();
            File[] files = file.listFiles(filter);
            if (files != null) {
                for (File subFile : files) {
                    __compress(key, subFile, zos, filter);
                }
            }
        }
    }

    public static byte[] gzip(byte[] bytes) {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(out);
            gzip.write(bytes);
            gzip.close();
            return out.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String gzip(String str) {
        byte[] bytes = gzip(str.getBytes());
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static byte[] ungzip(byte[] bytes) {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            GZIPInputStream gzip = new GZIPInputStream(in);
            byte[] buffer = new byte[1024];
            int offset;
            while ((offset = gzip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            gzip.close();
            return out.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String ungzip(String data) {
        byte[] dataBytes = Base64.getDecoder().decode(data.getBytes());
        byte[] bytes = ungzip(dataBytes);
        return new String(bytes);
    }

}
